package com.wx.house.terminal.api.wxpay.service.impl;

import com.wx.house.terminal.api.wxpay.sdk.WXPayConstants.SignType;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wx.house.common.base.ProjectConfiguration;
import com.wx.house.common.exception.BusinessException;
import com.wx.house.common.exception.UserException;
import com.wx.house.common.util.IDUtils;
import com.wx.house.core.mapper.capital.SysAdminDetailMapper;
import com.wx.house.core.mapper.expenses.ECollectFeesMapper;
import com.wx.house.core.mapper.expenses.ECostDetailMapper;
import com.wx.house.core.mapper.expenses.ECostMapper;
import com.wx.house.core.mapper.order.TOrderMapper;
import com.wx.house.core.pojo.po.ECollectFees;
import com.wx.house.core.pojo.po.ECost;
import com.wx.house.core.pojo.po.ECostDetail;
import com.wx.house.core.pojo.po.TOrder;
import com.wx.house.core.pojo.vo.ECostPameVo;
import com.wx.house.terminal.api.common.Constant;
import com.wx.house.terminal.api.wxpay.conf.WXConfigUtil;
import com.wx.house.terminal.api.wxpay.sdk.WXPay;
import com.wx.house.terminal.api.wxpay.sdk.WXPayConstants;
import com.wx.house.terminal.api.wxpay.sdk.WXPayUtil;
import com.wx.house.terminal.api.wxpay.service.WxService;
import com.wx.house.terminal.api.wxpay.vo.PayResultVo;
import lombok.extern.slf4j.Slf4j;

import org.apache.commons.lang3.ObjectUtils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@Slf4j
public class WxServiceImpl extends ServiceImpl<TOrderMapper, TOrder> implements WxService {
    public static  String SPBILL_CREATE_IP = "47.108.76.142";
    public static  String NOTIFY_URL = "http://housepay.9linked.com/pay/weixin/notify.json";
    public static  String TRADE_TYPE_APP = "APP";
    @Resource
    private TOrderMapper tOrderMapper;
    @Resource
    private ECostMapper eCostMapper;
    @Resource
    private ECostDetailMapper eCostDetailMapper;
    @Resource
    private ECollectFeesMapper eCollectFeesMapper;
    @Resource
    private SysAdminDetailMapper sysAdminDetailMapper;
    @Resource
    private  WxService wxService;
    @Resource 
    private ProjectConfiguration projectConfiguration;
    
    /**
     * 调用官方SDK 获取预支付订单等参数
     * @param attach 额外参数
     * @param totalFee 总价
     * @param outTradeNo 订单号
     * @return
     * @throws Exception
     */
    @Override
    public Map<String, String> dounifiedOrder(String attach, String totalFee,String outTradeNo) throws Exception {
        Map<String, String> returnMap = new HashMap<>();
        WXConfigUtil config = new WXConfigUtil();
        WXPay wxpay = new WXPay(config);
        Map<String, String> data = new HashMap<>();
        //生成商户订单号，不可重复
        data.put("appid", config.getAppID());
        data.put("mch_id", config.getMchID());
        data.put("nonce_str", WXPayUtil.generateNonceStr());
        String body = "订单支付";
        data.put("body", body);
        data.put("out_trade_no", outTradeNo);
        data.put("total_fee", totalFee);
        //自己的服务器IP地址
        data.put("spbill_create_ip", projectConfiguration.getStaticResource().getWeixinpaySpbillCreateIp());
        //异步通知地址（请注意必须是外网）
        data.put("notify_url", projectConfiguration.getStaticResource().getWeixinpayNotifyUrl());
        //交易类型
        data.put("trade_type", TRADE_TYPE_APP);
        //附加数据，在查询API和支付通知中原样返回，该字段主要用于商户携带订单的自定义数据
        data.put("attach", attach);
        String sign1 = WXPayUtil.generateSignature(data,config.getKey(), WXPayConstants.SignType.MD5);
        data.put("sign", sign1);
        try {
            //使用官方API请求预付订单
            Map<String, String> response = wxpay.unifiedOrder(data);
            String returnCode = response.get("return_code");    //获取返回码
            //若返回码为SUCCESS，则会返回一个result_code,再对该result_code进行判断
            if (returnCode.equals("SUCCESS")) {//主要返回以下5个参数
                String resultCode = response.get("result_code");
                returnMap.put("appid", response.get("appid"));
                returnMap.put("mch_id", response.get("mch_id"));
                returnMap.put("nonce_str", response.get("nonce_str"));
                returnMap.put("sign", response.get("sign"));
                if ("SUCCESS".equals(resultCode)) {//resultCode 为SUCCESS，才会返回prepay_id和trade_type
                    //获取预支付交易回话标志
                    returnMap.put("trade_type", response.get("trade_type"));
                    returnMap.put("prepay_id", response.get("prepay_id"));
                    //将prepay_id存入数据库
                    tOrderMapper.updatePrepayId(outTradeNo,response.get("prepay_id"));
                    return returnMap;
                } else {
                    //此时返回没有预付订单的数据
                    return returnMap;
                }
            } else {
                return returnMap;
            }
        } catch (Exception e) {
            System.out.println(e);
            //系统等其他错误的时候
        }
        return returnMap;
    }
    /**
     *
     * @param notifyData 异步通知后的XML数据
     * @return
     */
    @Override
    public String payBack(String notifyData) {
        WXConfigUtil config = null;
        try {
            config = new WXConfigUtil();
        } catch (Exception e) {
            e.printStackTrace();
        }
        WXPay wxpay = new WXPay(config);
        String xmlBack = "";
        Map<String, String> notifyMap = null;
            try {
            notifyMap = WXPayUtil.xmlToMap(notifyData);         // 调用官方SDK转换成map类型数据
            if (wxpay.isPayResultNotifySignatureValid(notifyMap)) {//验证签名是否有效，有效则进一步处理
                String return_code = notifyMap.get("return_code");//状态
                if (return_code.equals("SUCCESS")) {
                	  String out_trade_no = notifyMap.get("out_trade_no");//商户订单号
                    if (out_trade_no != null) {
                        // ###注意特殊情况：订单已经退款，但收到了支付结果成功的通知，不应把商户的订单状态从退款改成支付成功
                        // ###注意特殊情况：微信服务端同样的通知可能会多次发送给商户系统，所以数据持久化之前需要检查是否已经处理过了，处理了直接返回成功标志
                        // ###业务数据持久化
                        //检查订单是否已经支付完成
                        if(checkOrder(out_trade_no)== Constant.PAY_STATE_FINISHED)
                        {   //throw new BusinessException("该订单已完成支付，请勿重复支付");
                            xmlBack = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
                        }else{
                            wxService.updatePaySuccess(notifyMap,out_trade_no);
                            System.err.println("支付成功");
                            log.info("微信手机支付回调成功订单号:{}", out_trade_no);
                            xmlBack = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
                        }
                    } else {
                        log.info("微信手机支付回调失败订单号:{}", out_trade_no);
                        xmlBack = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
                    }
                }
                return xmlBack;
            } else {
                // 签名错误，如果数据里没有sign字段，也认为是签名错误
                //失败的数据要不要存储？
                log.error("手机支付回调通知签名错误");
                xmlBack = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
                return xmlBack;
            }
        } catch (Exception e) {
            log.error("手机支付回调通知失败", e);
            xmlBack = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
        }
        return xmlBack;
    }
    public void updatePaySuccess( Map<String, String> notifyMap,String out_trade_no) throws Exception{
        String total_fee = new DecimalFormat("0.00").format(new BigDecimal(notifyMap.get("total_fee")).divide(new BigDecimal(100)));//商户订单号
        String transaction_id = notifyMap.get("transaction_id");//商户订单号
        
        TOrder selectTorId = tOrderMapper.selectTorId(out_trade_no);
        if(selectTorId.getActualPrice().compareTo(new BigDecimal(total_fee))!=0) {
        	 throw new BusinessException("付款金额与订单金额不匹配");
        }
        
        Map<String, String> map=new HashMap<String, String>();
        map.put("outTradeNo",out_trade_no);
        map.put("price",total_fee);
        map.put("transactionId", transaction_id);
        int i = tOrderMapper.updateOrderId(map);
        if(i<=0){
            log.error("支付金额不匹配");
            //加入系统充值异常日志
        }else{
            //根据微信订单号查询需要更改状态的数据
            List<ECost> eCostList=eCostMapper.selectTransactionId(out_trade_no);
            //取出待处理费用Id表
            List<String> collect = eCostList.stream().map(ECost::getId).collect(Collectors.toList());
            for (int j = 0; j < eCostList.size(); j++) {
                //根据待处理id查询待处理详细
                List<ECostPameVo> list=eCostDetailMapper.selectECostID(eCostList.get(j).getId());
                //将数据存入收取费用表
                ECollectFees eCollectFees=null;
                for (int k = 0; k < list.size(); k++) {
                    eCollectFees = ECollectFees.builder()
                            .id(IDUtils.createUUId())
                            .szType(list.get(k).getCostType())
                            .feesType(list.get(k).getChargeType().intValue())
                            .paymentMethod(1)
                            .mAccId(list.get(k).getMAccId())
                            .contractId(list.get(k).getContrId())
                            .feeMonth(list.get(k).getChargeMonth())
                            .receivable(list.get(k).getReceiveMoney())
                            .actual(list.get(k).getReceiveMoney())
                            .createTime(list.get(k).getCreationTime())
                            .chargeTime(new Date())
                            .remarks(eCostList.get(j).getRemarks())
                            .build();
                    //添加到收支费用表
                    if(eCollectFeesMapper.insert(eCollectFees)!=1) {
                        throw new BusinessException("添加记录到收取费用表失败");
                    }
                    //更新待处理费用
                    ECost eCost =new ECost();
                    eCost.setId(list.get(k).getECostId());
                    eCost.setActualPay(list.get(k).getTotal());
                    eCost.setPayTime(new Date());
                    if(eCostMapper.updateById(eCost)!=1) {
                        throw new BusinessException("更新待处理费用处理状态失败");
                    }
                    ECostDetail eCostDetail = new ECostDetail();
                    eCostDetail.setId(list.get(k).getId());
                    eCostDetail.setNetMoney(list.get(k).getReceiveMoney());
                    eCostDetail.setCostState(1);
                    eCostDetail.setChargeTime(new Date());
                    if(eCostDetailMapper.updateById(eCostDetail)!=1){
                        throw new BusinessException("更新待处理费用详情处理状态失败");
                    }
                }
            }
            if(sysAdminDetailMapper.updateTotal(new BigDecimal(total_fee), eCostList.get(0).getMAccId())!=1) {
                throw new BusinessException("更新用户总收入，余额失败");
            }
        }
    }
    /***
     * 检查订单是否已经支付完成
     * @param outTradeNo
     * @return
     */
    private int checkOrder(String outTradeNo){
        QueryWrapper<TOrder> query=new QueryWrapper();
        query.eq("order_number",outTradeNo);
        query.eq("state", Constant.PAY_STATE_FINISHED);
        return tOrderMapper.selectCount(query);
    }
	@Resource
    WxService wXservice;
    public void test() {
			 String notifyData="<xml>" +
                     "<appid><![CDATA[wx1533fe63d0e1c96d]]></appid>" +
                     "<attach><![CDATA[{\"user_id\":\"fff9c25a59324c6ea3d39691afa8496f\",\"outTradeNo\":\"200107040414\"}]]></attach>" +
                     "<bank_type><![CDATA[OTHERS]]></bank_type>" +
                     "<cash_fee><![CDATA[1]]></cash_fee>" +
                     "<fee_type><![CDATA[CNY]]></fee_type>" +
                     "<is_subscribe><![CDATA[N]]></is_subscribe>" +
                     "<mch_id><![CDATA[1568110581]]></mch_id>" +
                     "<nonce_str><![CDATA[405ab47c201b4e8aa382dfa027c141e7]]></nonce_str>" +
                     "<openid><![CDATA[oVjt7v7p3t3WwmvDCnqfI0XW1Ack]]></openid>" +
                     "<out_trade_no><![CDATA[wxpay1578386099532]]></out_trade_no>" +
                     "<result_code><![CDATA[SUCCESS]]></result_code>" +
                     "<return_code><![CDATA[SUCCESS]]></return_code>" +
                     "<sign><![CDATA[02344C49856ACBB151768F3719B64CF6]]></sign>" +
                     "<time_end><![CDATA[20200107163521]]></time_end>" +
                     "<total_fee>1</total_fee>" +
                     "<trade_type><![CDATA[APP]]></trade_type>" +
                     "<transaction_id><![CDATA[4200000503202001072493164598]]></transaction_id>" +
                     "</xml>";
        wXservice.payBack(notifyData);
	}

    public PayResultVo payResult(String OrderNumber) throws Exception {
        //判断数据库里订单是否成功，成功直接返回，未支付状态查询稳信
        PayResultVo payResultVo = new PayResultVo();
        TOrder selectNumber = tOrderMapper.selectNumber(OrderNumber);
        if(selectNumber.getState()==1){
            payResultVo.setTotal_fee(selectNumber.getActualPrice());
            payResultVo.setTransaction_id(selectNumber.getTransactionId());
            payResultVo.setOut_trade_no(selectNumber.getOrderNumber());
            payResultVo.setTime_end(selectNumber.getPayTime());
        }else{
            Map<String, String> map = new HashMap<>();
            WXConfigUtil config = new WXConfigUtil();
            WXPay wxpay = new WXPay(config);
            map.put("out_trade_no",selectNumber.getOrderNumber());
            Map<String, String> resoutMap = wxpay.orderQuery(map);
            if(resoutMap.get("return_code").equals("SUCCESS")){
                if(resoutMap.get("result_code").equals("SUCCESS")){
                    if(resoutMap.get("trade_state").equals("SUCCESS")){
                    	DateFormat format= new SimpleDateFormat("yyyyMMddHHmmss");
                        payResultVo.setTotal_fee(new BigDecimal(resoutMap.get("total_fee")).divide(new BigDecimal(100.00)));
                        payResultVo.setTransaction_id(resoutMap.get("transaction_id"));
                        payResultVo.setTime_end(format.parse(resoutMap.get("time_end")));
                        payResultVo.setOut_trade_no(resoutMap.get("out_trade_no"));
                        
                        TOrder selectTorId = tOrderMapper.selectTorId(OrderNumber);
                        if(selectTorId.getActualPrice().compareTo(payResultVo.getTotal_fee())!=0) {
                        	 throw new BusinessException("付款金额与订单金额不匹配");
                        }
                        
                        //根据微信订单号查询需要更改状态的数据
                        List<ECost> eCostList=eCostMapper.selectTransactionId(payResultVo.getOut_trade_no());
                        //取出待处理费用Id表
                        List<String> collect = eCostList.stream().map(ECost::getId).collect(Collectors.toList());
                        for (int j = 0; j < eCostList.size(); j++) {
                            //根据待处理id查询待处理详细
                            List<ECostPameVo> list=eCostDetailMapper.selectECostID(eCostList.get(j).getId());
                            //将数据存入收取费用表
                            ECollectFees eCollectFees=null;
                            for (int k = 0; k < list.size(); k++) {
                                eCollectFees = ECollectFees.builder()
                                        .id(IDUtils.createUUId())
                                        .szType(list.get(k).getCostType())
                                        .feesType(list.get(k).getChargeType().intValue())
                                        .paymentMethod(1)
                                        .mAccId(list.get(k).getMAccId())
                                        .contractId(list.get(k).getContrId())
                                        .feeMonth(list.get(k).getChargeMonth())
                                        .receivable(list.get(k).getReceiveMoney())
                                        .actual(list.get(k).getReceiveMoney())
                                        .createTime(list.get(k).getCreationTime())
                                        .chargeTime(new Date())
                                        .remarks(eCostList.get(j).getRemarks())
                                        .build();
                                //添加到收支费用表
                                if(eCollectFeesMapper.insert(eCollectFees)!=1) {
                                    throw new BusinessException("添加记录到收取费用表失败");
                                }
                                //更新待处理费用
                                ECost eCost =new ECost();
                                eCost.setId(list.get(k).getECostId());
                                eCost.setActualPay(list.get(k).getTotal());
                                eCost.setPayTime(new Date());
                                if(eCostMapper.updateById(eCost)!=1) {
                                    throw new BusinessException("更新待处理费用处理状态失败");
                                }
                                ECostDetail eCostDetail = new ECostDetail();
                                eCostDetail.setId(list.get(k).getId());
                                eCostDetail.setNetMoney(list.get(k).getReceiveMoney());
                                eCostDetail.setCostState(1);
                                eCostDetail.setChargeTime(new Date());
                                if(eCostDetailMapper.updateById(eCostDetail)!=1){
                                    throw new BusinessException("更新待处理费用详情处理状态失败");
                                }
                            }
                        }
                        
                        //更新支付状态,paytime state total
                        if(tOrderMapper.updateState(payResultVo.getTotal_fee(), payResultVo.getTransaction_id(), OrderNumber)==0) {
                            throw new BusinessException("更新支付状态失败");
                        }
                        
                        if(sysAdminDetailMapper.updateTotal(payResultVo.getTotal_fee(), eCostList.get(0).getMAccId())!=1) {
                            throw new BusinessException("更新用户总收入，余额失败");
                        }
                    }
                }
            }
        }
        return payResultVo;
    }
}